import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { DataSource, In, Like, Not, Repository } from 'typeorm'
import { CreateStudentDto } from './dto/create-student.dto'
import { UpdateStudentDto } from './dto/update-student.dto'
import { QueryStudentDto } from './dto/query-student.dto'
import { StudentEntity, StudentStatus, StudentType } from './entities/student.entity'
import { paginate } from '~/helper/paginate'
import { StudentClassEntity } from './entities/student-class.entity'
import { UpdateStudentExpireDto } from './dto/update-student-expire.dto'
import { StudentExpireLogEntity } from './entities/student-expire-log.entity'
import { CreateStudentClassDto } from './dto/create-student-class.dto'
import { UpdateStudentClassDto } from './dto/update-student-class.dto'
import { UpdateStudentPasswordDto } from './dto/update-student-password.dto'
import { ClassEntity } from '~/modules/admin/manage/class/entities/class.entity'
import { format, startOfWeek, endOfWeek, startOfMonth, endOfMonth, startOfQuarter, endOfQuarter, isBefore, isAfter, differenceInDays } from 'date-fns'
import { OverallAttendanceStatisticsDto, OverallAttendancePeriodDto } from './dto/overall-attendance-statistics.dto'
import { InjectRedis } from '~/common/decorators/inject-redis.decorator'
import { Redis } from 'ioredis'
import { isEmpty, isNil } from 'lodash'
import { Pagination } from '~/helper/paginate/pagination'
import { createPaginationObject } from '~/helper/paginate/create-pagination'
import { md5, randomValue } from '~/utils'
import { RedisKeys } from '~/constants/cache.constant'
import { StudentWithClassInfoDto, ClassInfoDto } from './dto/student-with-class-info.dto'
import { StudentDetailDto, ClassDetailDto } from './dto/student-detail.dto'
import { StudentCheckinStatisticsDto, CheckinPeriodStatisticsDto } from './dto/student-checkin-statistics.dto'

// 定义增强的学生信息接口，包含班级和打卡信息
export interface EnhancedStudent extends StudentEntity {
  classes?: any[];
  totalCheckinDays?: number;
  totalCheckinRate?: string;
}

@Injectable()
export class StudentService {
  constructor(
    @InjectRepository(StudentEntity)
    private readonly studentRepository: Repository<StudentEntity>,
    @InjectRepository(StudentClassEntity)
    private readonly studentClassRepository: Repository<StudentClassEntity>,
    @InjectRepository(StudentExpireLogEntity)
    private readonly studentExpireLogRepository: Repository<StudentExpireLogEntity>,
    @InjectRedis() private readonly redis: Redis,
    @InjectRepository(ClassEntity)
    private readonly classRepository: Repository<ClassEntity>,
  ) {}

  /**
   * 生成学生ID
   * 格式：STU + 年月日 + 4位序号
   */
  async generateStudentId(): Promise<string> {
    const dateStr = format(new Date(), 'yyyyMMdd')
    const key = `${RedisKeys.STUDENT_ID_SEQ}${dateStr}`

    // 使用Redis自增获取序号
    const seq = await this.redis.incr(key)

    // 设置过期时间（2天后过期）
    await this.redis.expire(key, 60 * 60 * 24 * 2)

    // 格式化序号为4位数，不足前面补0
    const seqStr = seq.toString().padStart(4, '0')

    return `STU${dateStr}${seqStr}`
  }

  /**
   * 创建学生
   */
  async create(createStudentDto: CreateStudentDto): Promise<StudentEntity> {
    try {
      // 检查手机号是否已存在
      const existingStudent = await this.studentRepository.findOne({
        where: { phone: createStudentDto.phone, isDelete: 0 },
      });
      
      if (existingStudent) {
        throw new BadRequestException('手机号已存在');
      }
      
      // 生成学生ID
      const studentId = await this.generateStudentId();
      
      // 生成密码盐和加密密码
      const salt = randomValue(32);
      const password = md5(`${createStudentDto.password}${salt}`);
      
      // 准备学生数据
    const student = this.studentRepository.create({
      studentId,
        phone: createStudentDto.phone,
        userName: createStudentDto.userName,
      password,
      psalt: salt,
        userId: createStudentDto.userId,
        studentType: createStudentDto.studentType,
        startAt: createStudentDto.startAt,
        expireAt: createStudentDto.expireAt,
        status: createStudentDto.status || StudentStatus.STUDYING,
      });
      
      // 直接尝试保存，使用原始entityManager.save方法，它支持UPSERT
      const savedStudent = await this.studentRepository.manager.save(StudentEntity, student, {
        // 如果主键冲突，则执行更新
        reload: true
      });

    // 记录学生有效期日志
      await this.createExpireLog(savedStudent.id, createStudentDto.startAt, createStudentDto.expireAt);
      
      return savedStudent;
    } catch (error) {
      // 专门处理主键冲突错误
      if (error.message && error.message.includes('Duplicate entry') && error.message.includes('PRIMARY')) {
        console.log('检测到主键冲突，尝试更新记录...');
        
        // 从错误信息中提取ID
        const match = error.message.match(/Duplicate entry '(\d+)'/);
        if (match && match[1]) {
          const id = parseInt(match[1]);
          
          // 查找现有记录
          const existingStudent = await this.studentRepository.findOne({ 
            where: { id } 
          });
          
          if (existingStudent) {
            // 生成密码盐和加密密码
            const salt = existingStudent.psalt;
            const password = createStudentDto.password 
              ? md5(`${createStudentDto.password}${salt}`)
              : existingStudent.password;
            
            // 准备更新数据
            const updatedData = {
              ...existingStudent,
              phone: createStudentDto.phone,
              userName: createStudentDto.userName,
              password,
              userId: createStudentDto.userId,
              studentType: createStudentDto.studentType,
              startAt: createStudentDto.startAt,
              expireAt: createStudentDto.expireAt,
              status: createStudentDto.status || existingStudent.status,
            };
            
            // 更新记录
            const updatedStudent = await this.studentRepository.save(updatedData);
            
            // 记录学生有效期日志
            await this.createExpireLog(updatedStudent.id, createStudentDto.startAt, createStudentDto.expireAt);
            
            return updatedStudent;
          }
        }
      }
      
      // 重新抛出其他错误
      throw error;
    }
  }

  /**
   * 添加学生到班级
   */
  async addStudentToClass(createStudentClassDto: CreateStudentClassDto) {
    // 检查学生是否存在
    await this.findOne(createStudentClassDto.studentId)

    // 创建学生班级关联实体
    const studentClass = this.studentClassRepository.create(createStudentClassDto)

    // 保存学生班级关联信息
    return this.studentClassRepository.save(studentClass)
  }

  /**
   * 更新学生班级信息
   */
  async updateStudentClass(id: number, updateStudentClassDto: UpdateStudentClassDto) {
    const studentClass = await this.studentClassRepository.findOne({
      where: { id, isDelete: 0 },
    })

    if (!studentClass) {
      throw new NotFoundException(`学生班级关联ID为${id}的记录不存在`)
    }

    // 更新学生班级关联信息
    return this.studentClassRepository.save({
      ...studentClass,
      ...updateStudentClassDto,
    })
  }

  /**
   * 获取学生班级列表
   */
  async findStudentClasses(studentId: number) {
    return this.studentClassRepository.find({
      where: { studentId, isDelete: 0 },
    })
  }

  /**
   * 查询学生列表（分页）
   */
  async findAll(query: QueryStudentDto): Promise<Pagination<EnhancedStudent>> {
    const { page, pageSize, studentId, phone, userName, status, studentType } = query

    const queryBuilder = this.studentRepository
      .createQueryBuilder('student')
      .leftJoinAndSelect('c_user', 'user', 'student.userId = user.id')
      .where('student.isDelete = :isDelete', { isDelete: 0 })

    // 添加筛选条件
    if (studentId) {
      queryBuilder.andWhere('student.studentId LIKE :studentId', { studentId: `%${studentId}%` })
    }

    if (phone) {
      queryBuilder.andWhere('student.phone LIKE :phone', { phone: `%${phone}%` })
    }

    if (userName) {
      queryBuilder.andWhere('student.userName LIKE :userName', { userName: `%${userName}%` })
    }

    if (!isNil(status)) {
      queryBuilder.andWhere('student.status = :status', { status })
    }

    if (!isNil(studentType)) {
      queryBuilder.andWhere('student.studentType = :studentType', { studentType })
    }

    // 设置需要查询的用户字段
    queryBuilder.addSelect([
      'user.id as userId',
      'user.user_name as userRealName',
      'user.phone as userPhone',
      'user.guarder as guarder',
      'user.guarder_phone as guarderPhone',
      'user.guarder_relation as guarderRelation',
      'user.birthday as birthday',
      'user.grade as grade',
      'user.school as school',
    ])

    // 设置默认排序
    queryBuilder.orderBy('student.createdAt', 'DESC')

    // 执行分页查询
    const pagination = await paginate<StudentEntity>(queryBuilder, {
      page,
      pageSize,
    })

    // 获取所有学生ID
    const studentIds = pagination.items.map(student => student.id)
    
    // 准备增强后的学生列表
    let enhancedItems = [...pagination.items]
    
    if (studentIds.length > 0) {
      try {
        // 获取学生-班级关系
        const studentClasses = await this.studentClassRepository.find({
          where: { studentId: In(studentIds), isDelete: 0 },
        })

        // 批量获取班级信息
        const classIds = [...new Set(studentClasses.map(sc => sc.classId))]
        
        let classes = []
        let teachers = []
        
        if (classIds.length > 0) {
          // 使用原始SQL查询获取班级信息
          classes = await this.studentRepository.manager.query(
            `SELECT id, class_id as classId, class_name as className, class_teacher as classTeacher, start_at as startAt 
             FROM c_class WHERE id IN (?)`,
            [classIds]
          )
          
          // 获取教师信息
          const teacherIds = [...new Set(classes.map(cls => cls.classTeacher).filter(id => id))]
          
          if (teacherIds.length > 0) {
            // 使用sys_user表的nickname字段查询教师信息
            teachers = await this.studentRepository.manager.query(
              `SELECT id, nickname as teacherName FROM sys_user WHERE id IN (?)`,
              [teacherIds]
            )
          }
        }
        
        // 构建映射关系
        const classMap = {}
        classes.forEach(cls => {
          classMap[cls.id] = cls
        })
        
        const teacherMap = {}
        teachers.forEach(teacher => {
          teacherMap[teacher.id] = teacher
        })
        
        // 为每个学生添加班级和教师信息
        enhancedItems = enhancedItems.map(student => {
          // 查找该学生的班级关联信息
          const studentClassList = studentClasses.filter(sc => sc.studentId === student.id)
          
          // 总打卡次数和班级信息
          let totalCheckinDays = 0
          const classesInfo = []
          
          studentClassList.forEach(sc => {
            totalCheckinDays += sc.checkinDays || 0
            
            const classInfo = classMap[sc.classId]
            
            if (classInfo) {
              const teacherInfo = teacherMap[classInfo.classTeacher]
              
              // 计算打卡率
              let checkinRate = 0
              if (classInfo.startAt) {
                const startDate = new Date(classInfo.startAt)
                const today = new Date()
                const diffTime = Math.max(0, today.getTime() - startDate.getTime())
                const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
                
                if (diffDays > 0) {
                  checkinRate = Math.min(100, Math.round((sc.checkinDays || 0) / diffDays * 100))
                }
              }
              
              classesInfo.push({
                classId: classInfo.classId,
                className: classInfo.className,
                teacherName: teacherInfo ? teacherInfo.teacherName : '未分配',
                checkinDays: sc.checkinDays || 0,
                lastCheckinAt: sc.lastCheckinAt,
                joinAt: sc.joinAt,
                checkinRate: `${checkinRate}%`
              })
            }
          })
          
          // 计算整体打卡率
          let overallCheckinRate = 0
          const allClassStartDates = studentClassList
            .map(sc => classMap[sc.classId]?.startAt)
            .filter(date => date)
          
          if (allClassStartDates.length > 0) {
            // 取最早的开班日期
            const earliestStartDate = new Date(Math.min(...allClassStartDates.map(date => new Date(date).getTime())))
            const today = new Date()
            const diffTime = Math.max(0, today.getTime() - earliestStartDate.getTime())
            const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
            
            if (diffDays > 0) {
              overallCheckinRate = Math.min(100, Math.round(totalCheckinDays / diffDays * 100))
            }
          }
          
          // 创建新对象而不是修改原对象
          const enhancedStudent = new StudentEntity()
          
          // 复制原始学生属性
          Object.assign(enhancedStudent, student)
          
          // 添加增强属性
          Object.assign(enhancedStudent, {
            classes: classesInfo,
            totalCheckinDays,
            totalCheckinRate: `${overallCheckinRate}%`
          })
          
          return enhancedStudent
        })
      } catch (error) {
        console.error('获取学生关联信息失败:', error)
        // 即使出错也返回基本学生信息
      }
    }

    // 返回新的分页结果
    return createPaginationObject<EnhancedStudent>({
      items: enhancedItems as unknown as EnhancedStudent[],
      totalItems: pagination.meta.totalItems,
      currentPage: pagination.meta.currentPage,
      limit: pagination.meta.itemsPerPage
    })
  }

  /**
   * 查询单个学生
   */
  async findOne(id: number) {
    // 1. 查询基本学生信息
    const queryBuilder = this.studentRepository
      .createQueryBuilder('student')
      .leftJoinAndSelect('c_user', 'user', 'student.userId = user.id')
      .where('student.id = :id', { id })
      .andWhere('student.isDelete = :isDelete', { isDelete: 0 })
      .select([
        'student.*',
        'user.id as userId',
        'user.user_name as userRealName',
        'user.phone as userPhone',
        'user.guarder as guarder',
        'user.guarder_phone as guarderPhone',
        'user.guarder_relation as guarderRelation',
        'user.birthday as birthday',
        'user.grade as grade',
        'user.school as school',
        'user.city as city',
        'user.province as province'
      ])

    const student = await queryBuilder.getRawOne()

    if (!student) {
      throw new NotFoundException(`学生ID为${id}的记录不存在`)
    }

    // 2. 获取学生-班级关系
    const studentClasses = await this.studentClassRepository.find({
      where: { studentId: id, isDelete: 0 },
    })

    if (studentClasses.length > 0) {
      // 3. 获取班级信息
      const classIds = studentClasses.map(sc => sc.classId)
      const classes = await this.studentRepository.manager.query(
        `SELECT 
           id, 
           class_id as classId, 
           class_name as className, 
           class_grade as classGrade,
           class_teacher as classTeacher, 
           start_at as startAt,
           expire_at as expireAt,
           status,
           study_plan as studyPlan
         FROM c_class 
         WHERE id IN (?)`,
        [classIds]
      )

      // 4. 获取教师信息
      const teacherIds = [...new Set(classes.map(cls => cls.classTeacher).filter(id => id))]
      let teachers = []
      
      if (teacherIds.length > 0) {
        teachers = await this.studentRepository.manager.query(
          `SELECT id, nickname as teacherName FROM sys_user WHERE id IN (?)`,
          [teacherIds]
        )
      }

      // 5. 构建映射关系
      const classMap = {}
      classes.forEach(cls => {
        classMap[cls.id] = cls
      })
      
      const teacherMap = {}
      teachers.forEach(teacher => {
        teacherMap[teacher.id] = teacher
      })

      // 6. 构建增强的班级信息
      const classesInfo = []
      let totalCheckinDays = 0
      const now = new Date()
      
      studentClasses.forEach(sc => {
        totalCheckinDays += sc.checkinDays || 0
        
        const classInfo = classMap[sc.classId]
        
        if (classInfo) {
          const teacherInfo = teacherMap[classInfo.classTeacher]
          
          // 计算打卡率
          let checkinRate = 0
          if (classInfo.startAt) {
            const startDate = new Date(classInfo.startAt)
            const diffTime = Math.max(0, now.getTime() - startDate.getTime())
            const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
            
            if (diffDays > 0) {
              checkinRate = Math.min(100, Math.round((sc.checkinDays || 0) / diffDays * 100))
            }
          }
          
          // 处理学习计划
          let studyPlanItems = []
          if (classInfo.studyPlan) {
            studyPlanItems = classInfo.studyPlan.split(',').filter(item => item.trim())
          }
          
          classesInfo.push({
            classId: classInfo.classId,
            className: classInfo.className,
            classGrade: classInfo.classGrade,
            teacherName: teacherInfo ? teacherInfo.teacherName : '未分配',
            teacherId: classInfo.classTeacher,
            startAt: classInfo.startAt,
            expireAt: classInfo.expireAt,
            status: classInfo.status,
            studyPlan: studyPlanItems,
            checkinDays: sc.checkinDays || 0,
            lastCheckinAt: sc.lastCheckinAt,
            joinAt: sc.joinAt,
            checkinRate: `${checkinRate}%`
          })
        }
      })
      
      // 7. 计算整体打卡率
      let overallCheckinRate = 0
      const allClassStartDates = studentClasses
        .map(sc => classMap[sc.classId]?.startAt)
        .filter(date => date)
      
      if (allClassStartDates.length > 0) {
        // 取最早的开班日期
        const earliestStartDate = new Date(Math.min(...allClassStartDates.map(date => new Date(date).getTime())))
        const diffTime = Math.max(0, now.getTime() - earliestStartDate.getTime())
        const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
        
        if (diffDays > 0) {
          overallCheckinRate = Math.min(100, Math.round(totalCheckinDays / diffDays * 100))
        }
      }
      
      // 8. 将班级和打卡信息添加到学生信息中
      student.classes = classesInfo
      student.totalCheckinDays = totalCheckinDays
      student.totalCheckinRate = `${overallCheckinRate}%`
    } else {
      // 如果学生没有班级，设置默认值
      student.classes = []
      student.totalCheckinDays = 0
      student.totalCheckinRate = '0%'
    }

    return student
  }

  /**
   * 更新学生信息
   */
  async update(id: number, updateStudentDto: UpdateStudentDto) {
    try {
      const student = await this.findOne(id);

    // 检查是否有密码更新
    if (updateStudentDto.password) {
      // 更新密码
        const password = md5(`${updateStudentDto.password}${student.psalt}`);
        updateStudentDto.password = password;
    }

    // 检查是否需要更新有效期
      const needUpdateExpire = updateStudentDto.startAt || updateStudentDto.expireAt;

    // 更新学生信息
    const updatedStudent = await this.studentRepository.save({
      ...student,
      ...updateStudentDto,
      });

    // 如果有效期有变更，记录到日志表
    if (needUpdateExpire) {
        const startAt = updateStudentDto.startAt || student.startAt;
        const expireAt = updateStudentDto.expireAt || student.expireAt;
        await this.createExpireLog(id, startAt, expireAt);
      }
      
      return updatedStudent;
    } catch (error) {
      console.error('更新学生信息失败:', error);
      throw error;
    }
  }

  /**
   * 调整学生有效期
   */
  async updateExpire(id: number, updateExpireDto: UpdateStudentExpireDto) {
    try {
      // 查找学生是否存在，确保使用repository.findOne而不是自定义的findOne方法
      const student = await this.studentRepository.findOne({
        where: { id, isDelete: 0 }
      });

      if (!student) {
        throw new NotFoundException(`学生ID为${id}的记录不存在`);
      }

      // 准备更新数据，保留所有原有字段
      const updatedData = {
      ...student,
      startAt: updateExpireDto.startAt,
        expireAt: updateExpireDto.expireAt
      };

      // 使用update方法而不是save方法来确保执行UPDATE而非INSERT
      await this.studentRepository.update(id, {
        startAt: updateExpireDto.startAt,
        expireAt: updateExpireDto.expireAt
      });
      
      // 重新获取更新后的学生数据
      const updatedStudent = await this.studentRepository.findOne({
        where: { id }
      });

    // 记录有效期变更日志
      await this.createExpireLog(id, updateExpireDto.startAt, updateExpireDto.expireAt);

      return updatedStudent;
    } catch (error) {
      console.error('更新学生有效期失败:', error);
      
      // 如果是主键冲突错误，提供更明确的错误信息
      if (error.message && error.message.includes('Duplicate entry') && error.message.includes('PRIMARY')) {
        throw new BadRequestException(`更新学生有效期失败：数据库冲突，请检查您的操作是否合理`);
      }
      
      throw error;
    }
  }

  /**
   * 直接修改学生密码
   */
  async updatePassword(id: number, dto: UpdateStudentPasswordDto): Promise<void> {
    const student = await this.findOne(id)

    if (isEmpty(student)) {
      throw new NotFoundException(`学生ID为${id}的记录不存在`)
    }

    // 加密新密码
    const password = md5(`${dto.password}${student.psalt}`)

    // 更新密码
    await this.studentRepository.update(
      { id },
      { password },
    )
  }

  /**
   * 创建学生有效期日志
   */
  private async createExpireLog(studentId: number, startAt: Date, expireAt: Date) {
    const expireLog = this.studentExpireLogRepository.create({
      studentId,
      startAt,
      expireAt,
    })

    return this.studentExpireLogRepository.save(expireLog)
  }

  /**
   * 删除学生（逻辑删除）
   */
  async remove(id: number) {
    const student = await this.findOne(id)

    // 逻辑删除
    student.isDelete = 1
    student.status = StudentStatus.DISABLED

    return this.studentRepository.save(student)
  }

  /**
   * 获取新的学生ID
   */
  async getNewStudentId() {
    const id = await this.generateStudentId()
    return { id }
  }

  /**
   * 获取所有已分配班级学生的打卡统计
   */
  async getOverallAttendanceStatistics(): Promise<OverallAttendanceStatisticsDto> {
    const now = new Date();
    
    // 定义各时间段
    const weekStart = startOfWeek(now, { weekStartsOn: 1 }); // 周一开始
    const weekEnd = endOfWeek(now, { weekStartsOn: 1 });
    
    const monthStart = startOfMonth(now);
    const monthEnd = endOfMonth(now);
    
    const quarterStart = startOfQuarter(now);
    const quarterEnd = endOfQuarter(now);
    
    console.log(`计算时间段 - 周: ${weekStart.toISOString()} 到 ${weekEnd.toISOString()}`);
    console.log(`计算时间段 - 月: ${monthStart.toISOString()} 到 ${monthEnd.toISOString()}`);
    console.log(`计算时间段 - 季度: ${quarterStart.toISOString()} 到 ${quarterEnd.toISOString()}`);
    
    try {
      // 获取所有已分配班级的学生记录
      const studentClassRecords = await this.studentClassRepository.find({
        where: { isDelete: 0 }
      });
      
      if (!studentClassRecords || studentClassRecords.length === 0) {
        return this.createEmptyOverallStatistics();
      }
      
      // 获取所有关联的班级ID
      const classIds = [...new Set(studentClassRecords.map(record => record.classId))];
      
      // 获取所有班级信息
      const classEntities = await this.classRepository.find({
        where: { id: In(classIds) }
      });
      
      console.log(`找到 ${classEntities.length} 个班级，共 ${studentClassRecords.length} 条学生班级关联记录`);
      
      // 计算不同时间段的统计数据
      const totalStats = this.calculateOverallPeriodStatistics(classEntities, studentClassRecords, null, null);
      const quarterStats = this.calculateOverallPeriodStatistics(classEntities, studentClassRecords, quarterStart, quarterEnd);
      const monthStats = this.calculateOverallPeriodStatistics(classEntities, studentClassRecords, monthStart, monthEnd);
      const weekStats = this.calculateOverallPeriodStatistics(classEntities, studentClassRecords, weekStart, weekEnd);
      
      // 计算已分配班级的学生总数（去除重复）
      const uniqueStudentIds = [...new Set(studentClassRecords.map(record => record.studentId))];
      
      console.log(`总体统计: 实际打卡=${totalStats.actualCheckins}, 应打卡=${totalStats.theoreticalCheckins}, 打卡率=${totalStats.attendanceRate}`);
      console.log(`季度统计: 实际打卡=${quarterStats.actualCheckins}, 应打卡=${quarterStats.theoreticalCheckins}, 打卡率=${quarterStats.attendanceRate}`);
      console.log(`月度统计: 实际打卡=${monthStats.actualCheckins}, 应打卡=${monthStats.theoreticalCheckins}, 打卡率=${monthStats.attendanceRate}`);
      console.log(`周度统计: 实际打卡=${weekStats.actualCheckins}, 应打卡=${weekStats.theoreticalCheckins}, 打卡率=${weekStats.attendanceRate}`);
      
    return {
        totalStudents: uniqueStudentIds.length,
        totalClasses: classEntities.length,
        total: totalStats,
        quarter: quarterStats,
        month: monthStats,
        week: weekStats
      };
    } catch (error) {
      console.error(`获取所有已分配班级学生的打卡统计数据失败:`, error);
      return this.createEmptyOverallStatistics();
    }
  }
  
  /**
   * 创建空的整体统计数据对象
   */
  private createEmptyOverallStatistics(): OverallAttendanceStatisticsDto {
    const emptyPeriodStats: OverallAttendancePeriodDto = {
      actualCheckins: 0,
      theoreticalCheckins: 0,
      attendanceRate: '0%'
    };
    
    return {
      totalStudents: 0,
      totalClasses: 0,
      total: { ...emptyPeriodStats },
      quarter: { ...emptyPeriodStats },
      month: { ...emptyPeriodStats },
      week: { ...emptyPeriodStats }
    };
  }
  
  /**
   * 计算给定时间段内所有已分配班级学生的考勤统计数据
   */
  private calculateOverallPeriodStatistics(
    classEntities: ClassEntity[],
    studentClassRecords: StudentClassEntity[],
    periodStart: Date | null,
    periodEnd: Date | null
  ): OverallAttendancePeriodDto {
    let actualCheckins = 0;
    let theoreticalCheckins = 0;
    const now = new Date();
    
    // 筛选有效的班级（必须有开始时间和学习计划）
    const validClasses = classEntities.filter(cls => cls.startAt && cls.studyPlan);
    
    if (validClasses.length === 0) {
      return {
        actualCheckins: 0,
        theoreticalCheckins: 0,
        attendanceRate: '0%'
      };
    }
    
    // 对每个班级进行统计
    for (const classEntity of validClasses) {
      const classStartDate = new Date(classEntity.startAt);
      
      // 获取该班级的所有学生
      const classStudentRecords = studentClassRecords.filter(record => 
        record.classId === classEntity.id
      );
      
      if (classStudentRecords.length === 0) continue;
      
      // 当没有指定时间段时，使用全部学生记录
      let filteredRecords = classStudentRecords;
      
      // 有指定时间段时，只筛选班级开始时间在该时间段内或之前的记录
      if (periodStart && periodEnd) {
        // 如果班级开始时间晚于时间段结束时间，跳过该班级
        if (isAfter(classStartDate, periodEnd)) continue;
        
        // 否则保留所有该班级的学生记录
        filteredRecords = classStudentRecords;
      }
      
      if (filteredRecords.length === 0) continue;
      
      // 计算时间段
      let startDate = classStartDate;
      let endDate = now;
      
      // 如果指定了时间段，调整计算范围
      if (periodStart && isAfter(periodStart, classStartDate)) {
        startDate = periodStart;
      }
      
      if (periodEnd) {
        endDate = periodEnd;
      }
      
      // 如果开始日期在结束日期之后，跳过当前班级
      if (isAfter(startDate, endDate)) continue;
      
      // 计算天数
      const daysDiff = differenceInDays(endDate, startDate) + 1;
      
      // 学习计划项目数
      const studyPlanItems = classEntity.studyPlan.split(',').filter(item => item.trim()).length;
      
      if (studyPlanItems === 0) continue;
      
      // 总打卡天数 - 需要根据时间段进行估算
      let periodActualCheckins = 0;
      
      if (!periodStart || !periodEnd) {
        // 不限时间段时，使用总打卡天数
        periodActualCheckins = filteredRecords.reduce((sum, record) => sum + (record.checkinDays || 0), 0);
      } else {
        // 有时间段限制时，估算该时间段内的打卡天数
        // 获取班级总运行天数
        const totalClassDays = differenceInDays(
          classEntity.expireAt ? new Date(classEntity.expireAt) : now, 
          classStartDate
        ) + 1;
        
        // 获取时间段天数占总天数的比例
        const periodRatio = Math.min(1, daysDiff / Math.max(1, totalClassDays));
        
        // 按比例估算时间段内的打卡天数
        periodActualCheckins = Math.round(
          filteredRecords.reduce((sum, record) => sum + (record.checkinDays || 0), 0) * periodRatio
        );
      }
      
      actualCheckins += periodActualCheckins;
      
      // 班级理论应打卡次数 = 学生数 * 学习计划项目数 * 天数
      const classTheoreticalCheckins = filteredRecords.length * studyPlanItems * daysDiff;
      theoreticalCheckins += classTheoreticalCheckins;
    }
    
    // 计算打卡率
    const attendanceRate = theoreticalCheckins > 0 
      ? Math.min(100, Math.round((actualCheckins / theoreticalCheckins) * 100)) 
      : 0;
    
    return {
      actualCheckins,
      theoreticalCheckins,
      attendanceRate: `${attendanceRate}%`
    };
  }

  /**
   * 获取所有已分配班级的学生信息
   * 包含班级信息、打卡次数、打卡率等数据
   */
  async getStudentsWithClassInfo(): Promise<StudentWithClassInfoDto[]> {
    try {
      // 1. 获取所有学生班级关联记录
      const studentClassRecords = await this.studentClassRepository.find({
        where: { isDelete: 0 }
      });
      
      if (!studentClassRecords || studentClassRecords.length === 0) {
        return [];
      }
      
      // 2. 获取唯一学生ID列表
      const uniqueStudentIds = [...new Set(studentClassRecords.map(record => record.studentId))];
      
      // 3. 获取班级ID列表
      const classIds = [...new Set(studentClassRecords.map(record => record.classId))];
      
      // 4. 获取所有相关学生信息
      const students = await this.studentRepository.find({
        where: { id: In(uniqueStudentIds), isDelete: 0 }
      });
      
      // 5. 获取所有相关班级信息
      const classes = await this.classRepository.find({
        where: { id: In(classIds) }
      });
      
      // 6. 获取教师信息
      const teacherIds = [...new Set(classes.map(cls => cls.classTeacher).filter(id => id))];
      let teachers = [];
      
      if (teacherIds.length > 0) {
        // 使用原始SQL查询获取教师信息
        teachers = await this.studentRepository.manager.query(
          `SELECT id, nickname as teacherName FROM sys_user WHERE id IN (?)`,
          [teacherIds]
        );
      }
      
      // 7. 建立映射关系，方便快速查找
      const classMap = {};
      classes.forEach(cls => {
        classMap[cls.id] = cls;
      });
      
      const teacherMap = {};
      teachers.forEach(teacher => {
        teacherMap[teacher.id] = teacher;
      });
      
      const studentMap = {};
      students.forEach(student => {
        studentMap[student.id] = student;
      });
      
      // 8. 按学生ID分组班级关联记录
      const studentClassMap = {};
      studentClassRecords.forEach(record => {
        if (!studentClassMap[record.studentId]) {
          studentClassMap[record.studentId] = [];
        }
        studentClassMap[record.studentId].push(record);
      });
      
      // 9. 计算当前周和当前月的时间段
      const now = new Date();
      const weekStart = startOfWeek(now, { weekStartsOn: 1 }); // 周一开始
      const weekEnd = endOfWeek(now, { weekStartsOn: 1 });
      const weekDays = differenceInDays(weekEnd, weekStart) + 1;
      
      const monthStart = startOfMonth(now);
      const monthEnd = endOfMonth(now);
      const monthDays = differenceInDays(monthEnd, monthStart) + 1;
      
      // 10. 构建结果
      const result: StudentWithClassInfoDto[] = [];
      
      for (const studentId of uniqueStudentIds) {
        const student = studentMap[studentId];
        if (!student) continue;
        
        const studentClasses = studentClassMap[studentId] || [];
        if (studentClasses.length === 0) continue;
        
        // 计算总打卡次数
        const totalCheckinDays = studentClasses.reduce((sum, record) => sum + (record.checkinDays || 0), 0);
        
        // 计算总打卡率
        let totalCheckinRate = '0%';
        if (student.startAt) {
          const startDate = new Date(student.startAt);
          const totalDays = Math.max(1, differenceInDays(now, startDate) + 1);
          const rate = Math.min(100, Math.round((totalCheckinDays / totalDays) * 100));
          totalCheckinRate = `${rate}%`;
        }
        
        // 计算当周平均打卡
        let weeklyAverageCheckin = 0;
        if (weekDays > 0) {
          // 按当前周天数平均
          weeklyAverageCheckin = parseFloat((totalCheckinDays / studentClasses.length / weekDays).toFixed(2));
        }
        
        // 计算当月平均打卡
        let monthlyAverageCheckin = 0;
        if (monthDays > 0) {
          // 按当前月天数平均
          monthlyAverageCheckin = parseFloat((totalCheckinDays / studentClasses.length / monthDays).toFixed(2));
        }
        
        // 处理班级信息
        const classesInfo: ClassInfoDto[] = [];
        
        for (const classRecord of studentClasses) {
          const classEntity = classMap[classRecord.classId];
          if (!classEntity) continue;
          
          // 获取教师信息
          const teacher = teacherMap[classEntity.classTeacher] || { teacherName: '未分配' };
          
          // 计算该班级的打卡率
          let classCheckinRate = '0%';
          if (classRecord.joinAt) {
            const classStartDate = new Date(classRecord.joinAt);
            const classTotalDays = Math.max(1, differenceInDays(now, classStartDate) + 1);
            const classRate = Math.min(100, Math.round(((classRecord.checkinDays || 0) / classTotalDays) * 100));
            classCheckinRate = `${classRate}%`;
          }
          
          classesInfo.push({
            classId: classEntity.id,
            className: classEntity.className,
            teacherName: teacher.teacherName,
            joinAt: classRecord.joinAt,
            checkinDays: classRecord.checkinDays || 0,
            checkinRate: classCheckinRate
          });
        }
        
        // 创建结果对象
        const studentWithClasses: StudentWithClassInfoDto = {
          id: student.id,
          studentId: student.studentId,
          userName: student.userName,
          phone: student.phone,
          totalCheckinDays,
          totalCheckinRate,
          weeklyAverageCheckin,
          monthlyAverageCheckin,
          classes: classesInfo
        };
        
        result.push(studentWithClasses);
      }
      
      return result;
    } catch (error) {
      console.error('获取已分配班级学生信息失败:', error);
      return [];
    }
  }

  /**
   * 获取学生详细信息
   */
  async getStudentDetail(id: number): Promise<StudentDetailDto> {
    try {
      // 1. 获取原始学生数据
      const rawStudent = await this.findOne(id);
      
      if (!rawStudent) {
        throw new NotFoundException(`学生ID为${id}的记录不存在`);
      }
      
      console.log('Raw student data:', JSON.stringify(rawStudent, null, 2));
      
      // 2. 计算账号剩余时间和学习时长
      const now = new Date();
      let remainingTime = '0天';
      let studyDuration = '0天';
      let studyStartDate = '';
      
      // 学习时长计算
      if (rawStudent.start_at) {
        const startDate = new Date(rawStudent.start_at);
        console.log(`[学习时长计算] 学生开始时间: ${startDate.toISOString()}`);
        
        const now = new Date();
        console.log(`[学习时长计算] 当前时间: ${now.toISOString()}`);
        
        const studyStartDays = differenceInDays(now, startDate);
        console.log(`[学习时长计算] 计算得到的天数差: ${studyStartDays}`);
        
        if (studyStartDays >= 0) {
          const years = Math.floor(studyStartDays / 365);
          const days = studyStartDays % 365;
          console.log(`[学习时长计算] 转换为: ${years}年${days}天`);
          
          studyDuration = years > 0 ? `${years}年${days}天` : `${days}天`;
          studyStartDate = format(startDate, 'yyyy-MM-dd');
          
          console.log(`[学习时长计算] 最终学习时长: ${studyDuration}`);
          console.log(`[学习时长计算] 学习开始日期: ${studyStartDate}`);
        } else {
          console.log(`[学习时长计算] 天数差为负值 ${studyStartDays}，设置默认值`);
        }
      }
      
      // 剩余时间计算
      if (rawStudent.expire_at) {
        const expireDate = new Date(rawStudent.expire_at);
        console.log(`[剩余时间计算] 学生到期时间: ${expireDate.toISOString()}`);
        
        const now = new Date();
        console.log(`[剩余时间计算] 当前时间: ${now.toISOString()}`);
        
        const remainingDays = differenceInDays(expireDate, now);
        console.log(`[剩余时间计算] 计算得到的剩余天数: ${remainingDays}`);
        
        if (remainingDays > 0) {
          const years = Math.floor(remainingDays / 365);
          const days = remainingDays % 365;
          console.log(`[剩余时间计算] 转换为: ${years}年${days}天`);
          
          remainingTime = years > 0 ? `${years}年${days}天` : `${days}天`;
          console.log(`[剩余时间计算] 最终剩余时间: ${remainingTime}`);
        } else {
          remainingTime = '已过期';
          console.log(`[剩余时间计算] 已过期，剩余天数: ${remainingDays}`);
        }
      }
      
      // 3. 获取学习方式描述
      let studentTypeDesc = '未知';
      if (rawStudent.student_type === 1) {
        studentTypeDesc = '线上学习';
      } else if (rawStudent.student_type === 2) {
        studentTypeDesc = '线下学习';
      }
      
      // 4. 整理班级信息
      const classesInfo: ClassDetailDto[] = [];
      
      if (rawStudent.classes && Array.isArray(rawStudent.classes)) {
        for (const cls of rawStudent.classes) {
          // 提取classId的数字部分
          let classId = cls.classId;
          if (typeof classId === 'string' && classId.startsWith('CLS')) {
            try {
              classId = parseInt(classId.replace('CLS', ''), 10);
            } catch (e) {
              console.error('Parse classId error:', e);
            }
          }
          
          classesInfo.push({
            classId: typeof classId === 'number' ? classId : parseInt(classId),
            className: cls.className,
            teacherName: cls.teacherName || '未分配',
            joinAt: cls.joinAt
          });
        }
      }
      
      // 5. 构建详情对象
      const result: StudentDetailDto = {
        id: parseInt(rawStudent.id),
        studentId: rawStudent.student_id,
        userName: rawStudent.user_name,
        phone: rawStudent.phone,
        remainingTime,
        startAt: rawStudent.start_at,
        expireAt: rawStudent.expire_at,
        guarder: rawStudent.guarder || '',
        guarderPhone: rawStudent.guarderPhone || '',
        guarderRelation: this.getGuarderRelationDesc(rawStudent.guarderRelation),
        school: rawStudent.school || '',
        grade: this.getGradeDesc(rawStudent.grade),
        birthday: rawStudent.birthday,
        studentType: rawStudent.student_type,
        studentTypeDesc,
        studyDuration,
        studyStartDate,
        city: rawStudent.city || '',
        province: rawStudent.province || '',
        classes: classesInfo
      };
      
      console.log('Formatted student detail:', JSON.stringify(result, null, 2));
      
      return result;
    } catch (error) {
      console.error('获取学生详情失败:', error);
      throw error;
    }
  }

  /**
   * 获取用户信息
   */
  private async getUserInfo(userId: number) {
    try {
      // 查询c_user表获取用户信息
      const userInfo = await this.studentRepository.manager.query(
        `SELECT 
           guarder, 
           guarder_phone as guarderPhone, 
           guarder_relation as guarderRelation,
           school,
           grade,
           birthday,
           city,
           province
         FROM c_user 
         WHERE id = ? AND is_delete = 0`,
        [userId]
      );
      
      return userInfo && userInfo.length > 0 ? userInfo[0] : null;
    } catch (error) {
      console.error('获取用户信息失败', error);
      return null;
    }
  }

  /**
   * 获取监护人关系描述
   */
  private getGuarderRelationDesc(relation: number): string {
    switch (relation) {
      case 1: return '爸爸';
      case 2: return '妈妈';
      case 3: return '爷爷';
      case 4: return '奶奶';
      case 5: return '外公';
      case 6: return '外婆';
      default: return '其他';
    }
  }

  /**
   * 获取年级描述
   */
  private getGradeDesc(grade: number): string {
    if (grade === undefined || grade === null) return '';
    
    if (grade === 0) return '幼儿园';
    if (grade >= 1 && grade <= 6) return `小学${this.getChineseNumber(grade)}年级`;
    if (grade === 7) return '初中一年级';
    if (grade === 8) return '初中二年级';
    if (grade === 9) return '初中三年级';
    
    return `${grade}年级`;
  }

  /**
   * 数字转为中文年级
   */
  private getChineseNumber(num: number): string {
    const chineseNumbers = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十'];
    if (num >= 0 && num <= 10) {
      return chineseNumbers[num];
    }
    return num.toString();
  }

  /**
   * 获取单个学生的打卡统计数据
   */
  async getStudentCheckinStatistics(studentId: number): Promise<StudentCheckinStatisticsDto> {
    try {
      // 1. 查询学生基本信息
      const student = await this.studentRepository.findOne({
        where: { id: studentId, isDelete: 0 }
      });
      
      if (!student) {
        throw new NotFoundException(`学生ID为${studentId}的记录不存在`);
      }
      
      // 2. 获取学生的所有班级关联记录
      const studentClassRecords = await this.studentClassRepository.find({
        where: { studentId, isDelete: 0 }
      });
      
      if (!studentClassRecords || studentClassRecords.length === 0) {
        // 如果学生没有关联班级，返回空统计数据
        return this.createEmptyStudentCheckinStatistics(student);
      }
      
      // 3. 获取学生关联的所有班级信息
      const classIds = studentClassRecords.map(record => record.classId);
      const classes = await this.classRepository.find({
        where: { id: In(classIds) }
      });
      
      // 4. 定义时间段
      const now = new Date();
      const weekStart = startOfWeek(now, { weekStartsOn: 1 }); // 周一开始
      const weekEnd = endOfWeek(now, { weekStartsOn: 1 });
      
      const monthStart = startOfMonth(now);
      const monthEnd = endOfMonth(now);
      
      const quarterStart = startOfQuarter(now);
      const quarterEnd = endOfQuarter(now);
      
      // 5. 创建班级与学生关联记录的映射
      const classMap = {};
      classes.forEach(cls => {
        classMap[cls.id] = cls;
      });
      
      // 6. 计算各时间段的打卡统计
      const totalStats = this.calculateStudentPeriodCheckinStatistics(
        studentClassRecords, 
        classMap, 
        null, 
        null
      );
      
      const quarterStats = this.calculateStudentPeriodCheckinStatistics(
        studentClassRecords, 
        classMap, 
        quarterStart, 
        quarterEnd
      );
      
      const monthStats = this.calculateStudentPeriodCheckinStatistics(
        studentClassRecords, 
        classMap, 
        monthStart, 
        monthEnd
      );
      
      const weekStats = this.calculateStudentPeriodCheckinStatistics(
        studentClassRecords, 
        classMap, 
        weekStart, 
        weekEnd
      );
      
      // 7. 构建返回结果
      return {
        studentId: student.id,
        studentCode: student.studentId,
        userName: student.userName,
        total: totalStats,
        quarter: quarterStats,
        month: monthStats,
        week: weekStats
      };
      
    } catch (error) {
      console.error('获取学生打卡统计数据失败:', error);
      throw error;
    }
  }

  /**
   * 创建空的学生打卡统计数据
   */
  private createEmptyStudentCheckinStatistics(student: StudentEntity): StudentCheckinStatisticsDto {
    const emptyPeriodStats: CheckinPeriodStatisticsDto = {
      checkinCount: 0,
      checkinRate: '0%'
    };
    
    return {
      studentId: student.id,
      studentCode: student.studentId,
      userName: student.userName,
      total: { ...emptyPeriodStats },
      quarter: { ...emptyPeriodStats },
      month: { ...emptyPeriodStats },
      week: { ...emptyPeriodStats }
    };
  }

  /**
   * 计算指定时间段内学生的打卡统计数据
   */
  private calculateStudentPeriodCheckinStatistics(
    studentClassRecords: StudentClassEntity[],
    classMap: Record<number, ClassEntity>,
    periodStart: Date | null,
    periodEnd: Date | null
  ): CheckinPeriodStatisticsDto {
    const now = new Date();
    let totalCheckinDays = 0;
    let totalTheoreticalDays = 0;
    
    // 遍历学生的所有班级关联记录
    for (const record of studentClassRecords) {
      const classEntity = classMap[record.classId];
      if (!classEntity || !classEntity.startAt) continue;
      
      const classStartDate = new Date(classEntity.startAt);
      
      // 如果班级开始时间在结束时间之后，跳过此班级
      if (periodEnd && isAfter(classStartDate, periodEnd)) continue;
      
      // 计算实际打卡天数
      let actualCheckins = record.checkinDays || 0;
      
      // 如果指定了时间段，需要估算该时间段内的打卡天数
      if (periodStart || periodEnd) {
        // 获取班级的开始和结束日期
        let startDate = classStartDate;
        let endDate = classEntity.expireAt ? new Date(classEntity.expireAt) : now;
        
        // 调整计算时间范围
        if (periodStart && isAfter(periodStart, startDate)) {
          startDate = periodStart;
        }
        if (periodEnd && isBefore(periodEnd, endDate)) {
          endDate = periodEnd;
        }
        
        // 如果开始日期在结束日期之后，跳过当前班级
        if (isAfter(startDate, endDate)) continue;
        
        // 计算该时间段占总时间的比例
        const totalDays = Math.max(1, differenceInDays(now, classStartDate) + 1);
        const periodDays = Math.max(1, differenceInDays(endDate, startDate) + 1);
        const ratio = periodDays / totalDays;
        
        // 按比例估算该时间段内的打卡天数
        actualCheckins = Math.round(actualCheckins * ratio);
        
        // 计算理论应打卡天数
        totalTheoreticalDays += periodDays;
      } else {
        // 不限时间段，使用从班级开始到现在的总天数
        totalTheoreticalDays += Math.max(1, differenceInDays(now, classStartDate) + 1);
      }
      
      totalCheckinDays += actualCheckins;
    }
    
    // 计算打卡率
    const checkinRate = totalTheoreticalDays > 0 
      ? Math.min(100, Math.round((totalCheckinDays / totalTheoreticalDays) * 100)) 
      : 0;
    
    return {
      checkinCount: totalCheckinDays,
      checkinRate: `${checkinRate}%`
    };
  }
}
